package com.dashu.base.redis;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;

import static com.dashu.base.redis.RedisUtil.getJedis;
@Slf4j
public class  PublicLock {
    private static final Long TOTAL_WAIT_SENCODS = 10 * 1000L;// 10s获取不到算超时
    private static final Integer REPLY_WAIT_SENCODS = 5;// 重试获取锁间隔时间ms



    /**
     * 获取操作锁
     * 需要写线程去过时历史失败的锁
     * @return true 获取成功，false获取失败
     * @throws InterruptedException
     */
    public static boolean getLock(String lockKey, Long waitSencods) throws InterruptedException{
        long t1 = System.currentTimeMillis();
        boolean isSuccess = false;
        while(!isSuccess){
            if (System.currentTimeMillis() <= t1 + waitSencods) {
                isSuccess = tryLock(lockKey, lockKey);
                if(!isSuccess){
                    Thread.sleep(REPLY_WAIT_SENCODS);//sleep10ms
                }
            }else{//超过等待时间，返回失败
                break;
            }
        }
        return isSuccess;
    }

    /**
     * 获取操作锁
     * @return true 获取成功，false获取失败
     * @throws InterruptedException
     */
    public static boolean getLock(String lockKey) throws InterruptedException{
        Long waitSencods = TOTAL_WAIT_SENCODS;
        return getLock(lockKey, waitSencods);
    }
    /**
     * 释放锁
     */
    public static void freeLock(String lockKey){
        delKey(RedisLiveKeyConstants.LIVE_TEMP_DB, lockKey);
    }

    /**
     * 锁是否存在
     * @param lockKey
     * @return
     */
    public static boolean isLock(String lockKey) {
        return getHashFieldExist(lockKey, lockKey, 0);
    }
    /**
     * 尝试获取锁
     * @return
     */
    private static boolean tryLock(String lockKey, String field){
        boolean isSuccess = false;
        long setValue = setValueIfNotExist(lockKey, field, CommonConstants.YES);//返回1则成功
        if(setValue > 0){
            isSuccess = true;
        }
        return isSuccess;
    }

    /**
     * 用于做原子锁操作 HSETNX key field value
     * 将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在。
     * 若域 field 已经存在，该操作无效。
     * 如果 key 不存在，一个新哈希表被创建并执行 HSETNX 命令。
     * 设置成功，返回 1 。
     * 如果给定域已经存在且没有操作被执行，返回 0 。
     */
    public static Long setValueIfNotExist(String key, String field, String value) {
        Long val = 0L;
        Jedis jedis = getJedis();
        try {
            int seconds = 3 * 60;//3min
//			jedis.select(RedisLiveKeyConstants.LIVE_TEMP_DB);
            Long ttl = jedis.ttl(key);
            val = jedis.hsetnx(key, field, value);
            if (val > 0) {
                jedis.expire(key, seconds);
            } else {//防止设置过期时间失败
                if (ttl != null && (ttl == -1 || ttl > seconds)) {
                    //获得的是一个失效的key, 第一次还是失败，第二次就好了
                    jedis.del(key);
                }
            }
        } catch (JedisException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return val;
    }
    /**
     * 删除一个或多个key
     *
     * @since 1.0.0
     */
    public static void delKey(int db, String... key) {
        if (key == null || key.length < 1) {
            return;
        }
        Jedis jedis = getJedis();
        try {
//			jedis.select(db);
            jedis.del(key);
        } catch (JedisException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 获取field是否存在
     *
     * @param key   key
     * @param field field
     * @param db    db
     * @return boolean
     */
    public static boolean getHashFieldExist(String key, String field, int db) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(field)) {
            return false;
        }
        Jedis jedis = getJedis();
        try {
            return jedis.hexists(key, field);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return false;
    }


    public static void main(String[] args) {
        String lockKey = "10010";
        try {
//            if(PublicLock.getLock(lockKey)){
                System.out.println("上锁");
                //这里写代码逻辑，执行完后需要释放锁
                PublicLock.freeLock(lockKey);

                System.out.println("解锁");
//            }
        } catch (Exception e) {
            //产生异常也需要释放锁
            PublicLock.freeLock(lockKey);
            System.out.println(e);
//            LOGGER.error(e);
        }finally {
        }
    }
}